/*******************************************************************************************
Copyright (C) 2017 Mestrelab Research S.L. All rights reserved.

This file is part of the Mnova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*******************************************************************************************/

/*globals date2ISOString, serialization, settings, FileDialog, Dir, File, BinaryStream, Application, NMRSpectrum, MnUi, MessageBox, ProgressDialog, Str, TextStream, NMRPredictorParams, nmr, mainWindow*/

/*jslint plusplus: true, continue: true, indent: 4*/

function Import2DCSVGeneric_Engine() {
	"use strict";
}

Import2DCSVGeneric_Engine.getCurrentDate =	function getCurrentDate(aISO) {
	"use strict";
	var date;
	date = date2ISOString(new Date());
	if (aISO !== undefined && aISO) {
		date = date.replace(/\:/g, ".");
	}
	return date;
};


Import2DCSVGeneric_Engine.processCSVFile = function processCSVFile(aPathCSVFile, aStartColumnNumber, aLastColumnNumber, aStartRowNumber, aLastRowNumber, aStartPPMF1, aEndPPMF1, aFrequencyF1, aNucleusF1, aStartPPMF2, aEndPPMF2, aFrequencyF2, aNucleusF2, aBlockSize, aToken, aHeader, aProgressBar) {
	"use strict";

	var file, line, inStream, totalLines, aux2TotalLines, flagStartingPoint, lines, i, fields = [], startRow, startColumn, failedRows,
		failedValues, logFile, rowValues, startDate, spec, rowCount, progressBar, error, auxBuffer, currentConvertLine, lastRow, lastColumn,
		totalppmLines, flagBlankLine, flagBlankWarning, firstBlankLine, flagStopConvertingProcess, warning, invF1, invF2;

	logFile = "";
	error = false;
	warning = false;
	file = new File(aPathCSVFile);

	if (aHeader !== undefined && aHeader) {
		logFile += "Started on: " + this.getCurrentDate() + "\n";
		logFile += "Data Directory: " + file.absDirPath + "\n";
		logFile += "First Column: " + aStartColumnNumber + "\n";
		if (aLastColumnNumber !== -1) {
			logFile += "Last Column: " + aLastColumnNumber + "\n";
		} else {
			logFile += "Last Column: unlimited\n";
		}
		logFile += "First Row: " + aStartRowNumber + "\n";
		if (aLastRowNumber !== -1) {
			logFile += "Last Row: " + aLastRowNumber + "\n";
		} else {
			logFile += "Last Row: unlimited\n";
		}

		logFile += "F1 Start ppm: " + aStartPPMF1 + "\n";
		logFile += "F1 End ppm: " + aEndPPMF1 + "\n";
		logFile += "F1 Nucleus: " + aNucleusF1 + "\n";
		logFile += "F1 Frequency: " + aFrequencyF1 + " MHz \n";
		logFile += "F2 Start ppm: " + aStartPPMF2 + "\n";
		logFile += "F2 End ppm: " + aEndPPMF2 + "\n";
		logFile += "F2 Nucleus: " + aNucleusF2 + "\n";
		logFile += "F2 Frequency: " + aFrequencyF2 + " MHz \n";
	}

	//axis direction
	invF1 = aStartPPMF1 > aEndPPMF1;
	invF2 = aStartPPMF2 > aEndPPMF2;

	//Check rows interval
	if (aLastRowNumber !== -1 && aLastRowNumber < aStartRowNumber) {
		error = true;
		logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [ERROR]: The start CSV row must be greater than the last CSV row";
	}

	//Check columns interval
	if (aLastColumnNumber !== -1 && aLastColumnNumber < aStartColumnNumber) {
		error = true;
		logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [ERROR]: The start CSV column must be greater than the last CSV column";
	}

	//Check file
	if (!file.open(File.ReadOnly)) {
		logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [ERROR]: The file could not be opened";
	}

	//Init the process if we do not get critical errors
	if (!error) {

		//Maximum size in MB per file in order to save it at once
		//The performance of the script is closely related with this parameter
		//maxSize = 25;
		//size = file.size / 1000000;

		//For huge files define the number of lines will be buffered before write to disk
		//bufferMaxLines = 10000;

		//Open progressBar dialog
		if (aProgressBar !== undefined && aProgressBar) {
			progressBar = new ProgressDialog();
			progressBar.minimum = 0;
			progressBar.maximum = 100;
			progressBar.value = 0;
			progressBar.visible = true;
			progressBar.labelText = "Reading file: " + Str.fileNameFromPath(aPathCSVFile);
		}

		//Count the number of lines
		totalLines = 0;
		totalppmLines = 0;
		flagBlankLine = false;
		flagBlankWarning = false;
		firstBlankLine = -1;
		line = "";
		currentConvertLine = 0;
		startRow = aStartRowNumber;
		startColumn = aStartColumnNumber - 1;
		inStream = new BinaryStream(file);
		lastRow = aLastRowNumber;
		lastColumn = aLastColumnNumber;

		while (!inStream.atEnd() && (progressBar === undefined || !progressBar.wasCanceled)) {

			//Split by new lines reading byte by byte
			line += inStream.readBytes(aBlockSize).toString();
			while (!inStream.atEnd() && line[line.length - 1] !== "\n" && (progressBar === undefined || !progressBar.wasCanceled)) {
				line += inStream.readBytes(1).toString();
			}

			if (line.indexOf("\n") === -1 && !inStream.atEnd()) {
				continue;
			} else {
				line = line.replace(/\r/g, "");
				lines = line.split("\n");
			}

			//Remove the blank space created on previous iteration
			if (!inStream.atEnd()) {
				if (lines[lines.length - 1] === "") {
					lines.pop();
				}
			}

			//Get ppm lines. We assume that there are no blank lines in the data column.
			//If we meet a blank line then the data is finished. Hence we bear in mind the previous lines
			for (i = 0; i < lines.length && (progressBar === undefined || !progressBar.wasCanceled); i++) {
				totalLines++;
				if (lines[i].trim() === "") {
					flagBlankLine = true;
					if (firstBlankLine === -1) {
						firstBlankLine = totalLines;
					}
				} else {
					if (flagBlankLine) {
						flagBlankWarning = true;
					} else {
						totalppmLines++;
					}
				}
			}

			//Prepare next iteration
			line = "";
			if (aProgressBar !== undefined && aProgressBar) {
				progressBar.value = ((inStream.pos / file.size) * 100) * 0.5;
			}
		}

		//Warning for a blank line issue and display the number of the total rows
		if (flagBlankWarning && (firstBlankLine !== 1)) {
			if (lastRow === -1 || (lastRow !== -1 && lastRow > firstBlankLine)) {
				logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [ERROR]: The line " + firstBlankLine + " is blank. All the lines below have been ignored";
				error = true;
			}
		}
		logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [INFO] : The file has " + totalLines + " rows";

		//Get the expected rows by using the starting point defined by the user
		if (lastRow !== -1) {
			if (lastRow > totalLines) {
				logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [WARNING]: The number of the total rows [" + totalLines + "] is lower than the last row selected [" + lastRow + "]";
				lastRow = totalLines;
				warning = true;
			}
			if (lastRow >= firstBlankLine) {
				logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [WARNING]: The last row selected is below the blank line issue";
				lastRow = firstBlankLine - 1;
				warning = true;
			}
		} else {
			lastRow = totalppmLines;
		}
		aux2TotalLines = Math.abs(lastRow - startRow) + 1;

		if (aux2TotalLines > 0) {

			startDate = this.getCurrentDate();

			//Initalize vars
			line = "";
			inStream.pos = 0;
			failedRows = 0;
			failedValues = 0;
			auxBuffer = 0;
			flagStartingPoint = 0;
			flagStopConvertingProcess = false;
			rowCount = 0;
			if (aProgressBar !== undefined && aProgressBar) {
				progressBar.labelText = "Converting file: " + Str.fileNameFromPath(aPathCSVFile);
				progressBar.update();
			}

			//Read the file block by block by setting the pointer file to the beginning newly
			inStream = new BinaryStream(file);
			while (!inStream.atEnd() && (progressBar === undefined || !progressBar.wasCanceled) && !flagStopConvertingProcess) {

				//Split by new lines reading byte by byte
				line += inStream.readBytes(aBlockSize).toString();
				while (!inStream.atEnd() && line[line.length - 1] !== "\n" && (progressBar === undefined || !progressBar.wasCanceled) && !flagStopConvertingProcess) {
					line += inStream.readBytes(1).toString();
				}

				if (line.indexOf("\n") === -1 && !inStream.atEnd()) {
					continue;
				} else {
					line = line.replace(/\r/g, "");
					lines = line.split("\n");
				}

				//Remove the blank space created on previous iteration
				if (!inStream.atEnd()) {
					if (lines[lines.length - 1] === "") {
						lines.pop();
					}
				}

				//Process each line
				for (i = 0; i < lines.length && (progressBar === undefined || !progressBar.wasCanceled) && !flagStopConvertingProcess; i++) {

					line = lines[i];
					currentConvertLine++;

					//Check ending row or blank line in order to stop the process
					if (line.trim() === "") {
						flagStopConvertingProcess = true;
					} else if (currentConvertLine > lastRow) {
						flagStopConvertingProcess = true;
					} else {
						//Adapt to a generic way
						line = line.trim();
						line = line.replace(/[,\s\t]+/g, aToken);

						//Checking for starting moment
						if (startRow <= currentConvertLine) {
							if (aProgressBar !== undefined && aProgressBar) {
								progressBar.value = 50 + ((inStream.pos / file.size) * 100) * 0.5;
							}

							//Checking to start at the the specified starting point
							flagStartingPoint++;
							auxBuffer++;
							fields = line.split(aToken).map(Number);

							//Create spectrum on first pass
							if (startRow === currentConvertLine) {
								//Get the expected columns by using the starting point defined by the user
								if (lastColumn !== -1) {
									if (lastColumn >= fields.length) {
										logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [WARNING]: The number of the total columns [" + fields.length + "] is lower than the last column selected [" + lastColumn + "]";
										lastColumn = fields.length - 1;
										warning = true;
									}
								} else {
									lastColumn = fields.length - 1;
								}
								spec = this.createSpectrum(lastRow - startRow + 1, aNucleusF1, aFrequencyF1, aStartPPMF1, aEndPPMF1, lastColumn - startColumn + 1, aNucleusF2, aFrequencyF2, aStartPPMF2, aEndPPMF2, logFile, aPathCSVFile);
								nmr.beginModification(spec.spectrum);
							}

							//Columns related errors
							if (fields.length - 1 < lastColumn) {
								failedRows++;
								logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [ERROR] [Row " + currentConvertLine + "]: The number of columns is shorter than that in the starting row";
							} else if (startColumn >= fields.length - 1) {
								failedRows++;
								logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [ERROR] [Row " + currentConvertLine + "]: The selected starting column is greater than the number of columns";
							} else {
								rowValues = fields.slice(startColumn, startColumn + lastColumn + 1).reverse();
							}
						}
					}
					//Here the line should be added to the spectrum
					if (rowValues !== undefined && rowValues.length > 0) {
						this.writeRowToSpectrum(spec.spectrum, rowValues, rowCount, logFile, aPathCSVFile, invF1);
						rowCount++;
					}
				}
				line = "";
			}
		}

		nmr.endModification(spec.spectrum);
		spec.spectrum.fitToHeight();
		spec.spectrum.update();
		mainWindow.activeDocument.update();

		if (failedRows > 0 || failedValues > 0) {
			error = true;
		}

		file.close();
	}

	if (error && !warning) {
		logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [INFO]: File processed with errors";
	} else if (!error && warning) {
		logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [INFO]: File processed with warnings";
	} else if (error && warning) {
		logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [INFO]: File processed with warnings and errors";
	} else {
		logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [INFO]: File processed successfully";
	}

	if (aProgressBar !== undefined && aProgressBar) {
		if (progressBar.wasCanceled) {
			logFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [INFO]: Process cancelled by the user";
		}

		progressBar.value = 100;
		progressBar.visible = false;
	}

	return [error, logFile];
};

Import2DCSVGeneric_Engine.writeRowToSpectrum = function writeRowToSpectrum(aSpec, aRowData, aRow, aLogFile, aPathCSVFile, aInvF1) {
	"use strict";
	if (aRow < 0) {
		aLogFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [INFO]: Attempt to write to negative row";
		return;
	}
	if (aRow >= aSpec.count(1)) {
		aLogFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [INFO]: Attempt to write to beyond F1 limits";
		return;
	}
	if (aInvF1) {
		aSpec.setReal(aSpec.count(1) - aRow - 1, "all", aRowData);
	} else {
		aSpec.setReal(aRow, "all", aRowData);
	}
};

Import2DCSVGeneric_Engine.writeDataToFile = function writeDataToFile(aFilePath, aValue, aAppend) {
	"use strict";

	var file, stream;

	file = new File(aFilePath);
	if (file.exists && aAppend !== undefined && aAppend) {
		file.open(File.Append);
	} else {
		if (file.exists) {
			File.remove(aFilePath);
		}
		File.create(aFilePath);
		file.open(File.WriteOnly);
	}
	stream = new TextStream(file);
	stream.codec = "UTF-8";
	stream.write(aValue);
	stream.flush();
	file.close();
};

Import2DCSVGeneric_Engine.readFile = function readFile(aPath, aSize) {
	"use strict";

	var stream, data, file, all;

	file = new File(aPath);
	file.open(File.ReadOnly);
	stream = new BinaryStream(file);
	data = stream.readBytes(aSize);
	all = stream.atEnd();
	file.close();

	return [data.toString(), all];
};

Import2DCSVGeneric_Engine.createSpectrum = function createSpectrum(af1Size, af1nuc, af1mhz, af1ppm1, af1ppm2, af2Size, af2nuc, af2mhz, af2ppm1, af2ppm2, aLogFile, aPathCSVFile) {
	"use strict";
	var result, spectrum;

	result = {};

	if (af2Size > 1) {
		spectrum = new NMRSpectrum({
			1: {nucleus: af1nuc, frequency: af1mhz, from_ppm: af1ppm1, to_ppm: af1ppm2, size: af1Size},
			2: {nucleus: af2nuc, frequency: af2mhz, from_ppm: af2ppm1, to_ppm: af2ppm2, size: af2Size},
			filename: aPathCSVFile,
			origin: 'CSV file',
			title: ''
		}
			);
	} else {
		spectrum = new NMRSpectrum({
			1: {nucleus: af2nuc, frequency: af2mhz, from_ppm: af2ppm1, to_ppm: af2ppm2, size: af2Size},
			filename: aPathCSVFile,
			origin: 'CSV file',
			title: ''
		}
			);
	}
	if (spectrum.uuid === undefined) {
		aLogFile += "\n[" + this.getCurrentDate() + "] [" + aPathCSVFile + "] [ERROR]: The spectrum creation process has failed";
	} else {
		result.spectrum = spectrum;
	}
	return result;
};

function import2DCSVGeneric() {
	"use strict";

	var fileName, fileNames, openDir, isPpm1StartingPoint, dialog, showDialog, block, startColumnNumber, lastColumnNumber, statusLastColumn, i,
		showBar, logFilePath, useHeader, token, startRowNumber, lastRowNumber, statusLastRow, startPPMF1,
		endPPMF1, nucleusF1, frequencyF1, startPPMF2, endPPMF2,  nucleusF2, frequencyF2, lastDir,
		resultArray, displayLog, logFile, ppms,
		dirSetKey = "Import 2D CSV Generic/Last Directory",
		startRowNumberKey = "Import 2D CSV Generic/First Row Number",
		lastRowNumberKey = "Import 2D CSV Generic/Last Row Number",
		lastRowStatusKey = "Import 2D CSV Generic/Last Row Status",
		startColumnNumberKey = "Import 2D CSV Generic/First Column Number",
		lastColumnNumberKey = "Import 2D CSV Generic/Last Column Number",
		lastColumnStatusKey = "Import 2D CSV Generic/Last Column Status",
		ppm1F1RangeKey = "Import 2D CSV Generic/F1 ppm1 Range",
		ppm2F1RangeKey = "Import 2D CSV Generic/F1 ppm2 Range",
		frequencyF1Key = "Import 2D CSV Generic/F1 Frequency",
		nucleusF1Key = "Import 2D CSV Generic/F1 Nucleus",
		ppm1F2RangeKey = "Import 2D CSV Generic/F2 ppm1 Range",
		ppm2F2RangeKey = "Import 2D CSV Generic/F2 ppm2 Range",
		frequencyF2Key = "Import 2D CSV Generic/F2 Frequency",
		nucleusF2Key = "Import 2D CSV Generic/F2 Nucleus",
		logFilePathKey = "Import 2D CSV Generic/File Path Log File",
		showLogKey = "Import 2D CSV Generic/Show Log";

	function padNumber(aNumber, aWidth, aPaddingSymbol) {

		var number, result;

		aPaddingSymbol = aPaddingSymbol || '0';
		number = String(aNumber);
		if (number.length >= aWidth) {
			result =  number;
		} else {
			result = [];
			result.length = aWidth - number.length + 1;
			result = result.join(aPaddingSymbol) + number;
		}

		return result;
	}

	function printPreviewData(aOnlyFirstAndLast, aData, aAllFile) {

		var richTextNumbering, richTextData, data, numberingLength, result;

		richTextNumbering = "<font style=\"font-size: 8pt; font-family: 'Arial'; color: Red\">";
		richTextData = "<font style=\"font-size: 8pt; font-family: 'Arial'; color: Black\">";
		result = "";
		data = aData.toString().split("\n");
		numberingLength = data.length.toString().length;
		if (aOnlyFirstAndLast) {
			result += richTextNumbering + padNumber(1, numberingLength) + ".&nbsp;&nbsp;</font>";
			result += richTextData + data[0].replace(/\n/g, "") + "</font><br>";
			result += richTextData + "[........]" + "</font><br>";
			result += richTextNumbering + padNumber(data.length, numberingLength) + ".&nbsp;&nbsp;</font>";
			result += richTextData + data[data.length - 1].replace(/\n/g, "") + "</font>";
		} else {
			for (i = 0; i < data.length; i++) {
				result += richTextNumbering + padNumber((i + 1), numberingLength) + ".&nbsp;&nbsp;</font>";
				result += richTextData + data[i] + "</font><br>";
			}
		}
		if (!aAllFile) {
			result += richTextData + "[........]" + "</font><br>";
		}

		return result;
	}

	function getPreviewData(aPath, aSize) {

		var i, result, data, all;

		//Retrive the last complete line
		result = Import2DCSVGeneric_Engine.readFile(aPath, aSize);
		data = result[0].toString();
		all = result[1];
		for (i = data.length - 1; !all && 0 <= i; i--) {
			if (data[i] === "\n") {
				data = data.substring(0, i);
				break;
			}
		}

		return [data, all];
	}

	function previewFile() {

		var dialog, showDialog, data, size, progressBar, result,
			sizePreviewKey = "Import 2D CSV Generic/Size Preview";

		dialog = Application.loadUiFile('ricares:import2DCSVFileViewer.ui');
		dialog.show();

		//Signal management
		dialog.widgets.tbReloadPreview.clicked.connect(dialog, function () {

			var size, result, progressBar;

			//Get default preview data
			size = 1024 * dialog.widgets.spbxSize.value;
			if (dialog.widgets.spbxSize.value > 50) {
				progressBar = new ProgressDialog();
				progressBar.visible = true;
				progressBar.labelText = "Previewing file: " + Str.fileNameFromPath(fileNames[0]);
			}
			result = getPreviewData(fileNames[0], size);
			data = printPreviewData(false, result[0].toString(), result[1]);
			dialog.widgets.tePreviewData.html = data;
			if (progressBar !== undefined) {
				progressBar.visible = false;
			}
		});

		//Load Settings
		dialog.widgets.spbxSize.value =  settings.value(sizePreviewKey, 100);

		//Get default preview data
		size = 1024 * dialog.widgets.spbxSize.value;
		if (dialog.widgets.spbxSize.value > 50) {
			progressBar = new ProgressDialog();
			progressBar.visible = true;
			progressBar.labelText = "Previewing file: " + Str.fileNameFromPath(fileNames[0]);
		}
		result = getPreviewData(fileNames[0], size);
		data = printPreviewData(false, result[0].toString(), result[1]);
		dialog.widgets.tePreviewData.html = data;
		if (progressBar !== undefined) {
			progressBar.visible = false;
		}

		//Execute preview dialog
		showDialog = true;
		while (showDialog) {

			//Check if cancel button is pressed by user
			if (!dialog.exec()) {
				return;
			}
			showDialog = false;
		}

		//Save settings
		settings.setValue(sizePreviewKey, dialog.widgets.spbxSize.value);
	}

	//Get files by sorting alphabetically
	openDir = settings.value(dirSetKey, Dir.home());
	fileNames = FileDialog.getOpenFileNames("CSV Files(*.csv *.txt)", "", openDir);
	fileNames.sort(Dir.numericPathsCompare);

	//Configuration section
	block = 10000;
	token = ";";
	showBar = true;

	//Checking at least for one file
	if (!fileNames.length) {
		return;
	}

	function readPpmRanges(aFileName) {
		var stream, file, firstLine, secondLine, lastLine = "", fields = [], val_first, val_second, val_last, delta, exp_last, result, readingLastLine, symb;
		result = {};
		file = new File(aFileName);
		if (!file.open(File.ReadOnly)) {
			return;
		}
		stream = new TextStream(file);
		firstLine = stream.readLine();
		if (stream.atEnd()) {
			return;
		}
		secondLine = stream.readLine();
		//Read last line in the file
		stream = new BinaryStream(file);
		stream.pos = file.size - 1;
		readingLastLine = true;
		while (readingLastLine) {
			do {
				symb = stream.readBytes(1).toString();
				lastLine = symb + lastLine;
				stream.pos = stream.pos - 2;
			} while ((symb !== "\n") && (stream.pos > 0));
			lastLine = lastLine.trim();
			lastLine = lastLine.replace(/[,\s\t]+/g, ";");
			if (lastLine !== "") {
				readingLastLine = false;
			} else {
				lastLine = "";
			}
		}
		file.close();
		if (firstLine === "") {
			return;
		}
		firstLine = firstLine.trim();
		firstLine = firstLine.replace(/[,\s\t]+/g, ";");
		fields = firstLine.split(";");
		if (fields.length < 3) {
			return;
		}
		val_first = Number(fields[0]);
		val_second = Number(fields[1]);
		val_last = Number(fields[fields.length - 1]);
		if (isNaN(val_first) || isNaN(val_second) || isNaN(val_last)) {
			return;
		}
		delta = val_second - val_first;
		exp_last = val_first + (fields.length - 1) * delta;
		if (Math.abs((exp_last - val_last) / val_last) > 0.1) {
			return;
		}
		result.startF2 = val_last;
		result.endF2 = val_first;
		//process F1
		secondLine = secondLine.trim();
		secondLine = secondLine.replace(/[,\s\t]+/g, ";");
		fields = secondLine.split(";");
		val_first = Number(fields[0]);
		lastLine = lastLine.trim();
		lastLine = lastLine.replace(/[,\s\t]+/g, ";");
		fields = lastLine.split(";");
		val_last = Number(fields[0]);
		if (isNaN(val_first) || isNaN(val_last)) {
			return;
		}
		result.startF1 = val_last;
		result.endF1 = val_first;
		return result;
	}


	//Load GUI
	lastDir = new File(fileNames[0]).absDirPath;
	dialog = Application.loadUiFile('ricares:import2DCSVGeneric.ui');
	dialog.show();
	logFilePath = Dir.temp();
	logFilePath = logFilePath.replace(/\\/g, "/");
	if (logFilePath[logFilePath.length - 1] !== "/") {
		logFilePath += "/";
	}

	//----------------SIGNALS MANAGEMENT---------------------
	dialog.widgets.tbRangeDirectionF1.clicked.connect(dialog, function () {

		var aux;

		aux = dialog.widgets.spbF1ppm1.value;
		dialog.widgets.spbF1ppm1.value = dialog.widgets.spbF1ppm2.value;
		dialog.widgets.spbF1ppm2.value = aux;
	});

	dialog.widgets.tbRangeDirectionF2.clicked.connect(dialog, function () {

		var aux;

		aux = dialog.widgets.spbF2ppm1.value;
		dialog.widgets.spbF2ppm1.value = dialog.widgets.spbF2ppm2.value;
		dialog.widgets.spbF2ppm2.value = aux;
	});

	dialog.widgets.tbLog.clicked.connect(dialog, function () {
		var filename;
		filename = dialog.widgets.leLog.text;
		filename = FileDialog.getSaveFileName("*.log", "Choose file", filename, 4);
		dialog.widgets.leLog.text = filename;
	});

	dialog.widgets.tbFilePreview.clicked.connect(previewFile);

	//Load registry values
	isPpm1StartingPoint = true;
	dialog.widgets.spbStartCSVColumn.value = settings.value(startColumnNumberKey, 1);
	dialog.widgets.spbStartCSVRow.value = settings.value(startRowNumberKey, 1);
	dialog.widgets.spbLastCSVRow.value = settings.value(lastRowNumberKey, 16384);
	dialog.widgets.spbLastCSVColumn.value = settings.value(lastColumnNumberKey, 16384);
	dialog.widgets.cbLastRow.checked = settings.value(lastRowStatusKey, false);
	dialog.widgets.cbLastColumn.checked = settings.value(lastColumnStatusKey, false);
	dialog.widgets.spbF1ppm1.value = settings.value(ppm1F1RangeKey, 0);
	dialog.widgets.spbF1ppm2.value = settings.value(ppm2F1RangeKey, 0);
	dialog.widgets.leF1Nucleus.text = settings.value(nucleusF1Key, "");
	dialog.widgets.spbF1Frequency.value = settings.value(frequencyF1Key, 100);
	dialog.widgets.spbF2ppm1.value = settings.value(ppm1F2RangeKey, 0);
	dialog.widgets.spbF2ppm2.value = settings.value(ppm2F2RangeKey, 0);
	dialog.widgets.leF2Nucleus.text = settings.value(nucleusF2Key, "");
	dialog.widgets.spbF2Frequency.value = settings.value(frequencyF2Key, 100);
	dialog.widgets.leLog.text = settings.value(logFilePathKey, logFilePath + "import2DCSVGeneric.log");
	dialog.widgets.cbShowLog.checked =  settings.value(showLogKey, true);

	ppms = readPpmRanges(fileNames[0], dialog);
	if (ppms !== undefined) {
		dialog.widgets.spbF1ppm1.value = ppms.startF1;
		dialog.widgets.spbF1ppm2.value = ppms.endF1;
		dialog.widgets.spbF2ppm1.value = ppms.startF2;
		dialog.widgets.spbF2ppm2.value = ppms.endF2;
		dialog.widgets.spbStartCSVRow.value = 2;
		dialog.widgets.spbStartCSVColumn.value = 2;
	}

	//Execute dialog
	showDialog = true;
	while (showDialog) {

		//Check if cancel button is clicked by user
		if (!dialog.exec()) {
			return;
		}

		if (dialog.widgets.cbLastRow.checked) {
			if (dialog.widgets.spbStartCSVRow.value > dialog.widgets.spbLastCSVRow.value) {
				MessageBox.warning("The start CSV row must be greater than the last CSV row.");
				continue;
			}
		}

		if (dialog.widgets.cbLastColumn.checked) {
			if (dialog.widgets.spbStartCSVColumn.value > dialog.widgets.spbLastCSVColumn.value) {
				MessageBox.warning("The start CSV column must be greater than the last CSV column.");
				continue;
			}
		}

		if (dialog.widgets.spbF1ppm1.value === dialog.widgets.spbF1ppm2.value) {
			MessageBox.warning("Wrong ppm range in F1. The intervals must not be equal.");
			continue;
		}

		if (dialog.widgets.spbF2ppm1.value === dialog.widgets.spbF2ppm2.value) {
			MessageBox.warning("Wrong ppm range in F2. The intervals must not be equal.");
			continue;
		}

		if (dialog.widgets.leLog.text === "") {
			MessageBox.warning("You must set a valid log file path.");
			continue;
		} else if (!(new Dir(new File(dialog.widgets.leLog.text).absDirPath).exists)) {
			MessageBox.warning("The absolute directory of the log file path does not exist.");
			continue;
		}

		showDialog = false;
	}

	//Get parameter values from GUI
	startColumnNumber = dialog.widgets.spbStartCSVColumn.value;
	startRowNumber = dialog.widgets.spbStartCSVRow.value;
	lastRowNumber = dialog.widgets.spbLastCSVRow.value;
	lastColumnNumber = dialog.widgets.spbLastCSVColumn.value;
	frequencyF1 = dialog.widgets.spbF1Frequency.value;
	nucleusF1 = dialog.widgets.leF1Nucleus.text;
	startPPMF1 = dialog.widgets.spbF1ppm1.value;
	endPPMF1 = dialog.widgets.spbF1ppm2.value;
	frequencyF2 = dialog.widgets.spbF2Frequency.value;
	nucleusF2 = dialog.widgets.leF2Nucleus.text;
	startPPMF2 = dialog.widgets.spbF2ppm1.value;
	endPPMF2 = dialog.widgets.spbF2ppm2.value;
	displayLog = dialog.widgets.cbShowLog.checked;
	logFilePath = dialog.widgets.leLog.text;
	statusLastRow = dialog.widgets.cbLastRow.checked;
	statusLastColumn = dialog.widgets.cbLastColumn.checked;
	if (!statusLastRow) {
		lastRowNumber = -1;
	}
	if (!statusLastColumn) {
		lastColumnNumber = -1;
	}

	//Save settings to the registry
	settings.setValue(dirSetKey, lastDir);
	settings.setValue(startColumnNumberKey, dialog.widgets.spbStartCSVColumn.value);
	settings.setValue(startRowNumberKey, dialog.widgets.spbStartCSVRow.value);
	settings.setValue(lastRowNumberKey, dialog.widgets.spbLastCSVRow.value);
	settings.setValue(lastColumnNumberKey, dialog.widgets.spbLastCSVColumn.value);
	settings.setValue(lastRowStatusKey, dialog.widgets.cbLastRow.checked);
	settings.setValue(lastColumnStatusKey, dialog.widgets.cbLastColumn.checked);
	settings.setValue(ppm1F1RangeKey, dialog.widgets.spbF1ppm1.value);
	settings.setValue(ppm2F1RangeKey, dialog.widgets.spbF1ppm2.value);
	settings.setValue(frequencyF1Key, dialog.widgets.spbF1Frequency.value);
	settings.setValue(nucleusF1Key, dialog.widgets.leF1Nucleus.text);
	settings.setValue(ppm1F2RangeKey, dialog.widgets.spbF2ppm1.value);
	settings.setValue(ppm2F2RangeKey, dialog.widgets.spbF2ppm2.value);
	settings.setValue(frequencyF2Key, dialog.widgets.spbF2Frequency.value);
	settings.setValue(nucleusF2Key, dialog.widgets.leF2Nucleus.text);
	settings.setValue(logFilePathKey + "import2DCSVGeneric.log", dialog.widgets.leLog.text);
	settings.setValue(showLogKey, dialog.widgets.cbShowLog.checked);

	//Iterate trough the already sorted files
	//serialization.fileHistory = false;
	for (i = 0; i < fileNames.length; i++) {
		fileName = fileNames[i];

		//Call to engine
		if (i === 0) {
			useHeader = true;
			logFile = "";
		} else {
			useHeader = false;
		}
		resultArray = Import2DCSVGeneric_Engine.processCSVFile(fileName, startColumnNumber, lastColumnNumber, startRowNumber, lastRowNumber, startPPMF1, endPPMF1, frequencyF1, nucleusF1, startPPMF2, endPPMF2, frequencyF2, nucleusF2, block, token, useHeader, showBar);
		logFile += resultArray[1];
	}

	//Log file management
	Import2DCSVGeneric_Engine.writeDataToFile(logFilePath, logFile, false);
	if (displayLog) {
		dialog = Application.loadUiFile('ricares:import1DCSVGenericDialogInspection.ui');
		dialog.title = "Import 2D CSV Generic Log";
		dialog.show();
		dialog.widgets.teLog.plainText = logFile;
	}
}

if (this.MnUi && MnUi.scripts_nmr) {
	MnUi.scripts_nmr.scripts_nmr_Import2DCSVGeneric = import2DCSVGeneric;
}
